"""
MAESS数据备份调度器模块
提供备份任务的定时调度功能
"""

import asyncio
import time
from datetime import datetime
from typing import Dict, List, Optional, Any
import schedule

from loguru import logger
from .backup_manager import get_backup_manager, BackupType
from utils.exceptions import SchedulerError


class BackupScheduler:
    """备份调度器 - 单例模式"""
    
    _instance = None
    _lock = asyncio.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, "_initialized"):
            self._initialized: bool = False
            self._backup_manager = None
            self._scheduler_thread = None
            self._stop_event = asyncio.Event()
            self._tasks: Dict[str, schedule.Job] = {}
            self._running = False
            self._initialized = True
    
    async def initialize(self):
        """初始化备份调度器"""
        async with self._lock:
            if self._initialized:
                return
            
            try:
                self._backup_manager = await get_backup_manager().initialize()
                self._stop_event.clear()
                
                # 加载调度配置
                await self._load_schedules()
                
                logger.info("备份调度器初始化成功")
                self._initialized = True
                
            except Exception as e:
                logger.error(f"备份调度器初始化失败: {e}")
                raise
    
    async def _load_schedules(self):
        """加载备份调度配置"""
        # 从备份管理器获取配置
        config = self._backup_manager._config
        
        # 清除现有任务
        self._tasks.clear()
        
        # 配置数据库备份任务
        if config.db_backup_enabled:
            job = schedule.every().day.at(config.db_backup_schedule.split()[1])
            job.do(lambda: asyncio.run(self._backup_task_wrapper(BackupType.DATABASE)))
            self._tasks[BackupType.DATABASE] = job
            logger.info(f"已配置数据库备份任务: {config.db_backup_schedule}")
        
        # 配置Redis备份任务
        if config.redis_backup_enabled:
            job = schedule.every().day.at(config.redis_backup_schedule.split()[1])
            job.do(lambda: asyncio.run(self._backup_task_wrapper(BackupType.REDIS)))
            self._tasks[BackupType.REDIS] = job
            logger.info(f"已配置Redis备份任务: {config.redis_backup_schedule}")
        
        # 配置文件系统备份任务
        if config.fs_backup_enabled:
            job = schedule.every().day.at(config.fs_backup_schedule.split()[1])
            job.do(lambda: asyncio.run(self._backup_task_wrapper(BackupType.FILESYSTEM)))
            self._tasks[BackupType.FILESYSTEM] = job
            logger.info(f"已配置文件系统备份任务: {config.fs_backup_schedule}")
        
        # 配置完整备份任务（每周一次）
        job = schedule.every().monday.at("02:00")
        job.do(lambda: asyncio.run(self._backup_task_wrapper(BackupType.FULL)))
        self._tasks["weekly_full"] = job
        logger.info(f"已配置每周完整备份任务: 每周一 02:00")
    
    def start(self):
        """启动调度器"""
        if self._running:
            logger.warning("备份调度器已经在运行中")
            return
        
        if not self._initialized:
            raise SchedulerError("备份调度器尚未初始化")
        
        logger.info("启动备份调度器")
        self._running = True
        self._stop_event.clear()
        
        # 启动调度器线程
        self._scheduler_thread = asyncio.create_task(self._scheduler_loop())
    
    def stop(self):
        """停止调度器"""
        if not self._running:
            logger.warning("备份调度器未在运行")
            return
        
        logger.info("停止备份调度器")
        self._stop_event.set()
        
        # 等待调度器线程结束
        if self._scheduler_thread:
            asyncio.create_task(self._wait_for_scheduler_stop())
        
        self._running = False
    
    async def _wait_for_scheduler_stop(self):
        """等待调度器线程停止"""
        try:
            await asyncio.wait_for(self._scheduler_thread, timeout=10.0)
        except asyncio.TimeoutError:
            logger.warning("调度器线程停止超时")
    
    async def _scheduler_loop(self):
        """调度器主循环"""
        try:
            while not self._stop_event.is_set():
                # 运行所有待执行的任务
                schedule.run_pending()
                
                # 检查是否有新任务需要运行（避免长时间睡眠）
                next_run = schedule.next_run()
                if next_run:
                    wait_seconds = max(0, (next_run - datetime.now()).total_seconds())
                    # 最多等待60秒，以便可以及时响应停止事件
                    wait_seconds = min(wait_seconds, 60.0)
                else:
                    wait_seconds = 60.0
                
                # 等待下一个任务或停止事件
                try:
                    await asyncio.wait_for(
                        self._stop_event.wait(), 
                        timeout=wait_seconds
                    )
                except asyncio.TimeoutError:
                    pass  # 继续循环检查任务
                    
        except Exception as e:
            logger.error(f"调度器循环异常: {e}")
        finally:
            logger.info("备份调度器已停止")
    
    async def _backup_task_wrapper(self, backup_type: str):
        """备份任务包装器，处理异常和日志"""
        try:
            logger.info(f"开始执行自动备份任务: {backup_type}")
            record = await self._backup_manager.create_backup(backup_type)
            logger.info(f"自动备份任务完成: {backup_type}, 备份ID: {record.backup_id}")
        except Exception as e:
            logger.error(f"自动备份任务失败: {backup_type}, 错误: {e}")
    
    def add_backup_job(self, backup_type: str, cron_expression: str) -> bool:
        """
        添加备份任务
        
        Args:
            backup_type: 备份类型
            cron_expression: Cron表达式，格式："分 时 日 月 周"
        
        Returns:
            bool: 是否添加成功
        """
        try:
            # 解析cron表达式
            minute, hour, day, month, weekday = cron_expression.split()
            
            # 创建任务
            job = None
            if weekday != "*":
                # 基于星期的调度
                weekday_map = {
                    "0": "sunday", "1": "monday", "2": "tuesday",
                    "3": "wednesday", "4": "thursday", "5": "friday", "6": "saturday"
                }
                if weekday in weekday_map:
                    weekday_name = weekday_map[weekday]
                    job = getattr(schedule.every(), weekday_name).at(f"{hour}:{minute}")
            else:
                # 基于日期的调度
                job = schedule.every().day.at(f"{hour}:{minute}")
            
            if job:
                job.do(lambda: asyncio.run(self._backup_task_wrapper(backup_type)))
                self._tasks[f"{backup_type}_{int(time.time())}"] = job
                logger.info(f"已添加备份任务: {backup_type}, 表达式: {cron_expression}")
                return True
            
            return False
            
        except Exception as e:
            logger.error(f"添加备份任务失败: {e}")
            return False
    
    def remove_backup_job(self, job_id: str) -> bool:
        """
        移除备份任务
        
        Args:
            job_id: 任务ID
        
        Returns:
            bool: 是否移除成功
        """
        if job_id in self._tasks:
            del self._tasks[job_id]
            # 注意：schedule库不支持直接移除任务，这里只是从我们的记录中移除
            # 真正的移除需要重启调度器
            logger.info(f"已移除备份任务: {job_id}")
            return True
        return False
    
    def list_scheduled_jobs(self) -> List[Dict[str, Any]]:
        """
        列出所有计划任务
        
        Returns:
            List[Dict]: 任务列表
        """
        jobs = []
        for job_id, job in self._tasks.items():
            # 获取任务信息
            job_info = {
                "job_id": job_id,
                "next_run": str(job.next_run) if job.next_run else None,
                "interval": str(job.interval),
                "job_type": "unknown"
            }
            
            # 尝试确定任务类型
            if job_id.startswith(BackupType.DATABASE):
                job_info["job_type"] = "数据库备份"
            elif job_id.startswith(BackupType.REDIS):
                job_info["job_type"] = "Redis备份"
            elif job_id.startswith(BackupType.FILESYSTEM):
                job_info["job_type"] = "文件系统备份"
            elif job_id.startswith(BackupType.FULL) or job_id == "weekly_full":
                job_info["job_type"] = "完整备份"
            
            jobs.append(job_info)
        
        return jobs
    
    async def trigger_immediate_backup(self, backup_type: str = BackupType.FULL) -> Optional[str]:
        """
        立即触发备份
        
        Args:
            backup_type: 备份类型
        
        Returns:
            str: 备份ID，如果失败则返回None
        """
        try:
            logger.info(f"手动触发备份: {backup_type}")
            record = await self._backup_manager.create_backup(backup_type)
            logger.info(f"手动备份完成: {backup_type}, 备份ID: {record.backup_id}")
            return record.backup_id
        except Exception as e:
            logger.error(f"手动备份失败: {backup_type}, 错误: {e}")
            return None
    
    def get_status(self) -> Dict[str, Any]:
        """
        获取调度器状态
        
        Returns:
            Dict: 调度器状态信息
        """
        return {
            "running": self._running,
            "initialized": self._initialized,
            "task_count": len(self._tasks),
            "next_scheduled_job": str(schedule.next_run()) if schedule.next_run() else None
        }
    
    async def close(self):
        """关闭调度器"""
        self.stop()
        logger.info("备份调度器已关闭")


# 创建全局备份调度器实例
backup_scheduler = BackupScheduler()


# 辅助函数
def get_backup_scheduler() -> BackupScheduler:
    """获取备份调度器实例"""
    return backup_scheduler


async def initialize_backup_scheduler():
    """初始化备份调度器"""
    scheduler = get_backup_scheduler()
    await scheduler.initialize()
    return scheduler


async def start_backup_scheduler():
    """启动备份调度器"""
    scheduler = get_backup_scheduler()
    if not scheduler._initialized:
        await scheduler.initialize()
    scheduler.start()
    return scheduler


async def stop_backup_scheduler():
    """停止备份调度器"""
    scheduler = get_backup_scheduler()
    scheduler.stop()
    return scheduler


async def trigger_backup(backup_type: str = BackupType.FULL) -> Optional[str]:
    """触发备份的便捷函数"""
    scheduler = get_backup_scheduler()
    if not scheduler._initialized:
        await scheduler.initialize()
    return await scheduler.trigger_immediate_backup(backup_type)
